package com.hd.hdxgddcommon.utils;

import com.hd.hdxgddcommon.aop.entity.LogAnParse;
import org.springframework.context.expression.MethodBasedEvaluationContext;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 解析SPEL 表达式
 *
 * @author huxingnan
 * @date 2018/5/21 10:51
 */
public class SpelUtils {
    public static ThreadLocal<Map<String, Object>> returnObjectMap = new ThreadLocal<>();

    public static void addReturnMap(Map<String, Object> map) {
        returnObjectMap.set(map);
    }

    public static void addReturnMap(String key, Object val) {
        Map<String, Object> map = returnObjectMap.get();
        if (map == null) map = new HashMap<>();
        map.put(key, val);
        returnObjectMap.set(map);
    }

    public static String parse(String spel, Method method, Object[] args) {
        //获取被拦截方法参数名列表(使用Spring支持类库)
        LocalVariableTableParameterNameDiscoverer u =
                new LocalVariableTableParameterNameDiscoverer();
        String[] paraNameArr = u.getParameterNames(method);
        //使用SPEL进行key的解析
        ExpressionParser parser = new SpelExpressionParser();
        //SPEL上下文
        StandardEvaluationContext context = new StandardEvaluationContext();
        //把方法参数放入SPEL上下文中
        for (int i = 0; i < paraNameArr.length; i++) {
            context.setVariable(paraNameArr[i], args[i]);
        }
        return parser.parseExpression(spel).getValue(context, String.class);
    }

    /**
     * 支持 #p0 参数索引的表达式解析
     *
     * @param spel 表达式
     * @return 解析后的字符串
     */
    public static List<LogAnParse> parseReturn(List<LogAnParse> spel) {

        Map<String, Object> map = returnObjectMap.get();
        returnObjectMap.remove();
        if (map != null) {
            List<LogAnParse> logAnParses = new ArrayList<>();
            for (LogAnParse logAnParse : spel) {
                String parseName = logAnParse.getParseName(false);
                logAnParse.setValue(map.get(parseName));
                logAnParses.add(logAnParse);
            }
            return logAnParses;
        }
        return null;
    }

    public static List<LogAnParse> parse(Object rootObject, List<LogAnParse> spel, Method method, Object[] args) {
        //获取被拦截方法参数名列表(使用Spring支持类库)
        LocalVariableTableParameterNameDiscoverer u =
                new LocalVariableTableParameterNameDiscoverer();
        String[] paraNameArr = u.getParameterNames(method);
        Class<?>[] parameterTypes = method.getParameterTypes();

        //使用SPEL进行key的解析
        ExpressionParser parser = new SpelExpressionParser();
        //SPEL上下文
        StandardEvaluationContext context = new MethodBasedEvaluationContext(rootObject, method, args, u);
        //把方法参数放入SPEL上下文中
        for (int i = 0; i < paraNameArr.length; i++) {
            context.setVariable(paraNameArr[i], args[i]);
        }

        List<LogAnParse> logAnParses = new ArrayList<>();

        for (int i = 0; i < spel.size(); i++) {
            LogAnParse logAnParse = spel.get(i);
            String parseName = logAnParse.getParseName(true);
            try {
                Expression expression = parser.parseExpression(parseName);
                Object value = expression.getValue(context, parameterTypes[getTypeIndex(paraNameArr, parseName)]);
                logAnParse.setValue(value);
                logAnParses.add(logAnParse);
            } catch (Exception e) {
                e.printStackTrace();
                continue;
            }
        }
        return logAnParses;
    }

    private static int getTypeIndex(String[] paraNameArr, String paraName) {
        String temp = paraName;
        if (temp.startsWith("#")) temp = temp.substring(1);
        for (int i = 0; i < paraNameArr.length; i++) {
            if (paraNameArr[i].equals(temp)) return i;
        }
        return -1;

    }
}
